﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DateTimeSeries.cs" company="">
//   
// </copyright>
// <summary>
//   The time series.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Systemathics.FrameWork
{
    [Serializable]
    public class TimeSeries : EventArgs, IEnumerable
    {
        public Dictionary<DateTime, Decimal> TimeSerie { get; set;}
        public String Name{get; private set;}

        public TimeSeries()
        {
            TimeSerie = new Dictionary<DateTime, Decimal>();
        }
        public TimeSeries(String name)
        {
            Name = name;
            TimeSerie = new Dictionary<DateTime, Decimal>();
        }
        public Decimal this[int index]
        {
            get
            {
                try
                {
                    KeyValuePair<DateTime, Decimal> kvp = TimeSerie.ElementAt(index);
                    return kvp.Value;
                }
                catch (Exception)
                {
                    return 0m; 
                }
            }
        }
        public Decimal this[DateTime DateDateTime]
        {
            get
            {
                try
                {
                    if (TimeSerie.ContainsKey(DateDateTime))
                        return TimeSerie[DateDateTime];
                    return 0m;
                }
                catch (Exception)
                {
                    return 0m;
                }
            }
        }

        public void Add(DateTime dt, Decimal value)
        {
            if (!TimeSerie.ContainsKey(dt))
                TimeSerie.Add(dt, Math.Round(value, 5));
        }
        public Decimal Ago(int n)
        {
            try
            {
                KeyValuePair<DateTime, Decimal> kvp = TimeSerie.ElementAt(TimeSerie.Count - n - 1);
                return kvp.Value;
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public Decimal First()
        {
            try
            {
                return TimeSerie.First().Value;
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public Decimal Last()
        {
            try
            {
                return TimeSerie.Last().Value;
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public int Count()
        {
            try
            {
                return TimeSerie.Count;
            }
            catch (Exception)
            {
                return 0;
            }
        }
        public void Remove(DateTime dt)
        {
            TimeSerie.Remove(dt);
        }
        public Decimal Min()
        {
            return TimeSerie.Values.Take(TimeSerie.Count).Min();
        }
        public Decimal Min(int nElements)
        {
            try
            {
                return TimeSerie.Values.Reverse().Take(nElements).Min();
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public Decimal Max()
        {
            return TimeSerie.Values.Take(TimeSerie.Count).Max();
        }
        public Decimal Max(int nElements)
        {
            try
            {
                return  TimeSerie.Values.Reverse().Take(nElements).Max();
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public Decimal Sum(int nElements)
        {
            try
            {
                return TimeSerie.Values.Reverse().Take(nElements).Sum();
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        //public double Varaince(int nElements)
        //{
        //    try
        //    {
        //        int N = TimeSerie.Count;
        //        var query = TimeSerie.ToList().GetRange(N - nElements, nElements);

        //    }
        //    catch (Exception)
        //    {
        //        return double.NaN;
        //    }
        //}
        public TimeSeries GetPositiveSeries()
        {
            try
            {    
                return TimeSerie.Values.TakeWhile( b => b > 0 ) as TimeSeries;
            }
            catch (Exception)
            {
                return null;
            }
        }
        public TimeSeries GetNegativeSeries()
        {
            try
            {
                return TimeSerie.Values.TakeWhile(b => b < 0) as TimeSeries;
            }
            catch (Exception)
            {
                return null;
            }
        }
        public Decimal Average(int nElements)
        {
            try
            {
                int N = TimeSerie.Count;
                return TimeSerie.Values.Reverse().Take(nElements).Average();
            }
            catch (Exception)
            {
                return 0m;
            }
        }
        public Decimal Average()
        {
            return TimeSerie.Values.Take(TimeSerie.Count).Average();
        }
        public bool Contains(DateTime ts)
        {
            return TimeSerie.ContainsKey(ts);
        }
        public bool Contains(Decimal value)
        {
            return TimeSerie.ContainsValue(value);
        }
        public Cross Crosses(TimeSeries ts)
        {
            try
            {
                lock (this)
                {
                    if (ts.Count() > 1 && TimeSerie.Count > 1)
                    {
                        if (Ago(1) < ts.Ago(1) && Last() > ts.Ago(1))
                            return Cross.Above;
                        if (Ago(1) > ts.Ago(1) && Last() < ts.Ago(1))
                            return  Cross.Below;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new NotImplementedException();
            }

            return Cross.None;
        }
        public Cross Crosses(Decimal value)
        {
            try
            {
                lock (this)
                {
                    if (TimeSerie.Count > 1)
                    {
                        if (Ago(1) < value && Last() > value)
                            return Cross.Above;
                        if (Ago(1) > value && Last() < value)
                            return Cross.Below;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new NotImplementedException();
            }

            return Cross.None;
        }
        public Decimal Variance()
        {
            int n = TimeSerie.Count;
            if(n > 1)
            {
                Decimal sumOfSquares = 0;
                Decimal total = 0;
                foreach (Decimal d in TimeSerie.Values)
                {
                    total += Math.Abs(d);
                    sumOfSquares += d * d;
                }

                return ((n * sumOfSquares) - total * total) / (n * (n - 1));
            }
            return 0m;
        }
        public Decimal Variance(int nElements)
        {
            int n = TimeSerie.Count;

            if (TimeSerie.Count - nElements > 0)
            {
                Decimal sumOfSquares = 0;
                Decimal total = 0;
                foreach (Decimal d in TimeSerie.Values.Reverse().Take(nElements))
                {
                    total += Math.Abs(d);
                    sumOfSquares += d * d;
                }

                return ((n * sumOfSquares) - total * total) / (n * (n - 1));
            }
            return 0m;
        }

        public Decimal StdDev()
        {
            int n = TimeSerie.Count;
            if (n > 1)
            {
                Decimal sumOfSquares = 0;
                Decimal total = 0;
                foreach (Decimal d in TimeSerie.Values)
                {
                    total += Math.Abs(d);
                    sumOfSquares += d * d;
                }
                return (Decimal)Math.Sqrt((double) ((n*sumOfSquares) - total*total)/(n*(n - 1)));
            }
            return 0m;
        }
        public Decimal StdDev(int nElements)
        {
            int n = TimeSerie.Count;
            if (TimeSerie.Count - nElements > 0)
            {
                Decimal sumOfSquares = 0;
                Decimal total = 0;
                foreach (Decimal d in TimeSerie.Values.Reverse().Take(nElements))
                {
                    total += Math.Abs(d);
                    sumOfSquares += d * d;
                }
                return (Decimal)Math.Sqrt((double)((n * sumOfSquares) - total * total) / (n * (n - 1)));
            }
            return 0m;
        }
        public void Clear()
        {
            TimeSerie.Clear();
        }

        #region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int j = 0; j < TimeSerie.Count; j++)
                yield return TimeSerie.ElementAt(j);
        }

        #endregion

        #region operators

        public static bool operator ==(TimeSeries series1, TimeSeries series2)
        {
            try
            {
                if (series1.Count() == series2.Count())
                {
                    foreach (var kvp in series1.TimeSerie)
                    {
                        if (kvp.Value != series2.TimeSerie[kvp.Key])
                            return false;
                    }

                    return true;
                }

                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }
        public override bool Equals(object obj)
        {
            return this == (TimeSeries) obj;
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        public static bool operator !=(TimeSeries series1, TimeSeries series2)
        {
            return !(series1 == series2);
        }
        public static TimeSeries operator +(TimeSeries series1, TimeSeries series2)
        {
            var ts = new TimeSeries();
            if (series1.Count() == series2.Count())
            {
                foreach (var kvp  in series1.TimeSerie)
                    ts.Add(kvp.Key, kvp.Value + series2.TimeSerie[kvp.Key]);
            }

            return ts;
        }
        public static TimeSeries operator +(TimeSeries series1, Decimal value)
        {
            var ts = new TimeSeries();
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, kvp.Value + value);
            return ts;
        }
        public static TimeSeries operator +(Decimal value, TimeSeries series1)
        {
            return series1 + value;
        }
        public static TimeSeries operator -(TimeSeries series1, TimeSeries series2)
        {
            var ts = new TimeSeries();
            if (series1.Count() == series2.Count())
            {
                foreach (var kvp in series1.TimeSerie)
                    ts.Add(kvp.Key, kvp.Value - series2.TimeSerie[kvp.Key]);
            }

            return ts;
        }
        public static TimeSeries operator -(TimeSeries series1, Decimal value)
        {
            var ts = new TimeSeries();
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, kvp.Value - value);
            return ts;
        }
        public static TimeSeries operator -(Decimal value, TimeSeries series1)
        {
            var ts = new TimeSeries();
            
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, value - kvp.Value);
            return ts;
        }
        public static TimeSeries operator *(TimeSeries series1, TimeSeries series2)
        {
            var ts = new TimeSeries();
            
            if (series1.Count() == series2.Count())
            {
                foreach (var kvp in series1.TimeSerie)
                    ts.Add(kvp.Key, kvp.Value*series2.TimeSerie[kvp.Key]);
            }

            return ts;
        }
        public static TimeSeries operator *(TimeSeries series1, Decimal value)
        {
            var ts = new TimeSeries();
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, kvp.Value*value);
            return ts;
        }
        public static TimeSeries operator *(Decimal value, TimeSeries series1)
        {
            return series1*value;
        }
        public static TimeSeries operator /(TimeSeries series1, TimeSeries series2)
        {
            var ts = new TimeSeries();
            if (series1.Count() == series2.Count())
            {
                foreach (var kvp in series1.TimeSerie)
                    ts.Add(kvp.Key, kvp.Value/series2.TimeSerie[kvp.Key]);
            }

            return ts;
        }
        public static TimeSeries operator /(TimeSeries series1, Decimal value)
        {
            var ts = new TimeSeries();
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, kvp.Value/value);
            return ts;
        }
        public static TimeSeries operator /(Decimal value, TimeSeries series1)
        {
            var ts = new TimeSeries();
            foreach (var kvp in series1.TimeSerie)
                ts.Add(kvp.Key, value/kvp.Value);
            return ts;
        }

        #endregion operators

        /*
        public double GetAutoCorrelation(int Lag);
        public virtual double GetAutoCovariance(int Lag);
        public override double GetCorrelation(DateTimeSeries series);
        public override double GetCorrelation(int row1, int row2, int index1, int index2);
        public override double GetCovariance(DateTimeSeries series);
        public override double GetCovariance(int row1, int row2, int index1, int index2);
        public virtual DoubleSeries GetPercentReturnSeries();
        public virtual DoubleSeries GetReturnSeries();

        */
    }
}